package com.autonavi.yunda.yunji.core.config.dingding;

import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.classic.spi.ThrowableProxy;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import com.autonavi.yunda.yunji.core.utils.AoneUtil;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.MDC;
import org.slf4j.helpers.MarkerIgnoringBase;

import java.net.InetAddress;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static com.autonavi.yunda.yunji.common.constant.CommonConstant.MDC_TRACE_ID;

/**
 * @author cizhong.wcs
 * @date 2022/12/19 7:38 下午
 */
public class DingDingErrorLogAppender extends UnsynchronizedAppenderBase<LoggingEvent> {
    private static final String WEBHOOK = "https://oapi.dingtalk.com/robot/send?access_token=bad0faea5b400af9008576f0551951ad4d80d9c804274e781282b043e6c136a4";

    private static final LoadingCache<String, AtomicInteger> LOG_COUNTER = CacheBuilder
            .newBuilder()
            .maximumSize(1000)
            .expireAfterAccess(5, TimeUnit.MINUTES)
            .build(new CacheLoader<>() {
                @NotNull
                @Override
                public AtomicInteger load(String s) {
                    return new AtomicInteger(0);
                }
            });

    private static final Set<Integer> ALERT_LOG_COUNTS = Set.of(0, 1, 2, 8, 16, 32, 64, 128, 256);

    public DingDingErrorLogAppender() {
    }


    @Override
    protected void append(LoggingEvent event) {
        String className = event.getLoggerName();
        // 如果是 DingTalkTool 的日志，不进行输出，否则网络出错时会引起无限递归
        if (DingTalkRobot.class.getName().equals(className) || MarkerIgnoringBase.class.getName().equals(className)) {
            return;
        }
        Integer count = getLogCount(className);
        if (!checkSendToDingDing(count)) {
            return;
        }
        String ip = "";
        try {
            ip = InetAddress.getLocalHost().getHostAddress();
        } catch (Exception ignored) {
        }
        StringBuilder content = new StringBuilder(2048);
        content.append("### (").append(AoneUtil.isPrepub() ? "预发环境" : "线上环境").append(")").append(AoneUtil.getAppName()).append("错误日志").append("\n\n");
        if (count > 2) {
            content.append("**报警抑制启动,当前错误已出现次数：**").append(count).append("\n\n");
        }
        content.append("**ip：**").append(ip).append("\n\n")
                .append("**location：**").append(className)
                .append("(traceId=").append(MDC.get(MDC_TRACE_ID)).append(')').append("\n\n")
                .append("**message：**").append(event.getMessage());

        Throwable ex = Optional.of(event)
                .map(e -> (ThrowableProxy) e.getThrowableProxy())
                .map(ThrowableProxy::getThrowable)
                .orElse(null);
        // 存在异常信息
        if (ex != null) {
            String stackTrace = ExceptionUtils.getStackTrace(ex);
            if (stackTrace.length() > 1000) {
                content.append("\n\n").append("**stackTrace：**").append(stackTrace, 0, 1000);
            } else {
                content.append("\n\n").append("**stackTrace：**").append(stackTrace);
            }
        }
        DingTalkRobot.sendMessage(WEBHOOK, AoneUtil.getAppName() + "错误日志", content.toString());
    }

    private static boolean checkSendToDingDing(Integer count) {
        if (ALERT_LOG_COUNTS.contains(count)) {
            return true;
        } else if (count > 256) {
            return checkSendToDingDing(count % 256);
        } else {
            return false;
        }
    }

    private static Integer getLogCount(String key) {
        try {
            AtomicInteger atomicInteger = LOG_COUNTER.get(key);
            return atomicInteger.getAndIncrement();
        } catch (ExecutionException e) {
            System.out.println("DingDingErrorLogAppender get cache error ,key = " + key);
            return 0;
        }
    }
}
